<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
 
class User_login
{
	var $_banned;
	var $_ban_reason;
	var $_auth_error;
	
	public function __construct()
	{
		$this->ci =& get_instance();
		log_message('debug', 'Auth Initialized');

		$this->ci->load->library('Session');
		$this->ci->load->helper('global');
		
		$this->autologin();
		$this->email_activation = $this->ci->config->item('email_activation');
		$this->allow_registration = $this->ci->config->item('allow_registration');
		$this->captcha_registration = $this->ci->config->item('captcha_registration');
		$this->captcha_login = $this->ci->config->item('captcha_login');
	}
	
	// 设置最后登录IP和最后登录时间
	public function _set_last_ip_and_last_login($user_id)
	{
		$data = array();
		if ($this->ci->config->item('login_record_ip'))
		{	
			$data['last_ip'] = $this->ci->input->ip_address();
		}
		
		if ($this->ci->config->item('login_record_time'))
		{
			$data['last_login'] = date('Y-m-d H:i:s', time());
		}
		
		if ( ! empty($data))
		{
			$this->ci->load->model('user/users', 'users');
			$this->ci->users->set_user($user_id, $data);
		}
	}
	
	// 开始登录错误尝试
	public function _increase_login_attempt()
	{
		if ($this->ci->config->item('count_login_attempts') AND ! $this->is_max_login_attempts_exceeded())
		{
			$this->ci->load->model('user/login_attempts', 'login_attempts');		
			$this->ci->login_attempts->increase_attempt($this->ci->input->ip_address());
		}
	}

	// 清空登录尝试
	public function _clear_login_attempts()
	{
		if ($this->ci->config->item('count_login_attempts'))
		{
			$this->ci->load->model('user/login_attempts', 'login_attempts');		
			$this->ci->login_attempts->clear_attempts($this->ci->input->ip_address());
		}
	}

	// 检查是否登录尝试超过最大的登录尝试中指定的配置
	public function is_max_login_attempts_exceeded()
	{
		$this->ci->load->model('user/login_attempts', 'login_attempts');
		
		return ($this->ci->login_attempts->check_attempts($this->ci->input->ip_address())->num_rows() >= $this->ci->config->item('max_login_attempts'));
	}

	// 获得角色ID，用于_set_session()功能
	public function _get_role_data($role_id)
	{
		$this->ci->load->model('user/roles', 'roles');
		$this->ci->load->model('user/permissions', 'permissions');
	
		$role_name = '';
		$parent_roles_id = array();
		$parent_roles_name = array();
		$permission = array();
		$parent_permissions = array();
		
		$query = $this->ci->roles->get_role_by_id($role_id);
		
		if ($query->num_rows() > 0)
		{
			$role = $query->row();

			$role_name = $role->name;
			
			//搜索用户role_id有parent_id > 0 做递归直到parent_id达到0。
			
			if ($role->parent_id > 0)
			{
				$parent_roles_id[] = $role->parent_id;
				$finished = FALSE;
				$parent_id = $role->parent_id;				

				while ($finished == FALSE)
				{
					$i_query = $this->ci->roles->get_role_by_id($parent_id);
					
					if ($i_query->num_rows() > 0)
					{
						$i_role = $i_query->row();
						
						if ($i_role->parent_id == 0)
						{
							$parent_roles_name[] = $i_role->name;
							$finished = TRUE;
						}
						else
						{
							$parent_id = $i_role->parent_id;
							
							$parent_roles_id[] = $parent_id;
							$parent_roles_name[] = $i_role->name;
						}
					}
					else
					{
						array_pop($parent_roles_id);
						$finished = TRUE;
					}
				}			
			}
		}
		
		$permission = $this->ci->permissions->get_permission_data($role_id);
		
		// 父权限
		if ( ! empty($parent_roles_id))
		{
			$parent_permissions = $this->ci->permissions->get_permissions_data($parent_roles_id);
		}

		$data['role_name'] = $role_name;
		$data['parent_roles_id'] = $parent_roles_id;
		$data['parent_roles_name'] = $parent_roles_name;
		$data['permission'] = $permission;
		$data['parent_permissions'] = $parent_permissions;
		
		return $data;
	}

	/* 自动注册相关函数 */
	public function _create_autologin($user_id)
	{
		$result = FALSE;
		
		$user = array(
			'key_id' => substr(md5(uniqid(rand().$this->ci->input->cookie($this->ci->config->item('sess_cookie_name')))), 0, 16),
			'user_id' => $user_id
		);
		
		$this->ci->load->model('user/user_autologin', 'user_autologin');

		//删除键
		$this->ci->user_autologin->prune_keys($user['user_id']);

		if ($this->ci->user_autologin->store_key($user['key_id'], $user['user_id']))
		{
			$this->_auto_cookie($user);
			$result = TRUE;
		}

		return $result;
	}

	public function autologin()
	{
		$result = FALSE;
		
		if ($auto = $this->ci->input->cookie($this->ci->config->item('autologin_cookie_name')) AND ! $this->ci->session->userdata('logged_in'))
		{
			$auto = unserialize($auto);
			
			if (isset($auto['key_id']) AND $auto['key_id'] AND $auto['user_id'])
			{		
				$this->ci->load->model('user/user_autologin', 'user_autologin');

				$query = $this->ci->user_autologin->get_key($auto['key_id'], $auto['user_id']);								
				if ($result = $query->row())
				{
					$this->_set_session($result);
					$this->_auto_cookie($auto);
					$this->_set_last_ip_and_last_login($auto['user_id']);
					$result = TRUE;
				}
			}
		}
		
		return $result;
	}

	public function _set_session($data)
	{
		$role_data = $this->_get_role_data($data->role_id);
		$user = array(						
			'user_id'						=> $data->id,
			'username'						=> $data->username,
			'role_id'						=> $data->role_id,			
			'role_name'						=> $role_data['role_name'],
			'parent_roles_id'				=> $role_data['parent_roles_id'],	// Array of parent role_id
			'parent_roles_name'				=> $role_data['parent_roles_name'], // Array of parent role_name
			'permission'					=> $role_data['permission'],
			'parent_permissions'			=> $role_data['parent_permissions'],			
			'logged_in'						=> TRUE
		);

		$this->ci->session->set_userdata($user);
	}

	public function _auto_cookie($data)
	{
		$this->ci->load->helper('cookie');

		$cookie = array(
			'name' 		=> $this->ci->config->item('autologin_cookie_name'),
			'value'		=> serialize($data),
			'expire'	=> $this->ci->config->item('autologin_cookie_life')
		);

		set_cookie($cookie);
	}

	/* End of Auto login related function */
	
	/* Helper function */
	
	public function check_uri_permissions($allow = TRUE)
	{
		if ($this->is_logged_in())
		{
			// If user is not admin
			if ( ! $this->is_admin())
			{
				// Get variable from current URI
				$controller = '/'.$this->ci->uri->rsegment(1).'/';
				if ($this->ci->uri->rsegment(2) != '')
				{
					$action = $controller.$this->ci->uri->rsegment(2).'/';
				}
				else
				{
					$action = $controller.'index/';
				}
				
				// Get URI permissions from role and all parents
				// Note: URI permissions is saved in 'uri' key
				$roles_allowed_uris = $this->get_permissions_value('uri');
				
				// Variable to determine if URI found
				$have_access = ! $allow;
				// Loop each roles URI permissions
				foreach ($roles_allowed_uris as $allowed_uris)
				{										
					if ($allowed_uris != NULL)
					{
						// Check if user allowed to access URI
						if (_array_in_array(array('/', $controller, $action), $allowed_uris))
						{
								$have_access = $allow;
								// Stop loop
								break;
						}
					}
				}
				
				// 检查URL权限事件$this->get_user_id(), $have_access
				// 空
				
				if ( ! $have_access)
				{
					redirect('', 'location');
				}				
			}
		}
		else
		{
			redirect('user/login', 'location');
		}
	}
	
	/*
		Get permission value from specified key.
		Call this function only when user is logged in already.
		$key is permission array key (Note: permissions is saved as array in table).
		If $check_parent is TRUE means if permission value not found in user role, it will try to get permission value from parent role.
		Returning value if permission found, otherwise returning NULL
	*/
	public function get_permission_value($key, $check_parent = TRUE)
	{
		$result = NULL;

		$permission = $this->ci->session->userdata('permission');
		
		// Check if key is in user permission array
		if (array_key_exists($key, $permission))
		{
			$result = $permission[$key];
		}
		else
		{
			if ($check_parent)
			{
				// Get current user parent permissions
				$parent_permissions = $this->ci->session->userdata('parent_permissions');
				
				// Check parent permissions array				
				foreach ($parent_permissions as $permission)
				{
					if (array_key_exists($key, $permission))
					{
						$result = $permission[$key];
						break;
					}
				}
			}
		}
		
		// 触发事件$this->get_user_id(), $key
		
		return $result;
	}
	
	/*
		Get permissions value from specified key.
		Call this function only when user is logged in already.
		This will get user permission, and it's parents permissions.
				
		$array_key = 'default'. Array ordered using 0, 1, 2 as array key.
		$array_key = 'role_id'. Array ordered using role_id as array key.
		$array_key = 'role_name'. Array ordered using role_name as array key.
		
		Returning array of value if permission found, otherwise returning NULL.
	*/
	public function get_permissions_value($key, $array_key = 'default')
	{
		$result = array();
		
		$role_id = $this->ci->session->userdata('role_id');
		$role_name = $this->ci->session->userdata('role_name');
		
		$parent_roles_id = $this->ci->session->userdata('parent_roles_id');
		$parent_roles_name = $this->ci->session->userdata('parent_roles_name');
		
		// Get current user permission
		$value = $this->get_permission_value($key, FALSE);
		
		if ($array_key == 'role_id')
		{
			$result[$role_id] = $value;
		}
		elseif ($array_key == 'role_name')
		{
			$result[$role_name] = $value;
		}
		else
		{
			array_push($result, $value);
		}
		
		// Get current user parent permissions
		$parent_permissions = $this->ci->session->userdata('parent_permissions');
		
		$i = 0;
		foreach ($parent_permissions as $permission)
		{
			if (array_key_exists($key, $permission))
			{
				$value = $permission[$key];
			}
			
			if ($array_key == 'role_id')
			{
				// It's safe to use $parents_roles_id[$i] because array order is same with permission array
				$result[$parent_roles_id[$i]] = $value;
			}
			elseif ($array_key == 'role_name')
			{
				// It's safe to use $parents_roles_name[$i] because array order is same with permission array
				$result[$parent_roles_name[$i]] = $value;
			}			
			else
			{
				array_push($result, $value);
			}
			
			$i++;
		}
		
		return $result;
	}

	public function get_user_info($user_id,$info)
	{
		$this->ci->load->model('user/users', 'users');
		$query = $this->ci->users->get_user_by_id($user_id);
		if ($query->num_rows() == 1)
		{
			$user_info = $query->row_array();
		}
		
		return $user_info[$info];
	}

	public function get_user_id()
	{
		return $this->ci->session->userdata('user_id');
	}

	public function get_username()
	{
		return $this->ci->session->userdata('username');
	}
	
	public function get_role_id()
	{
		return $this->ci->session->userdata('role_id');
	}
	
	public function get_role_name()
	{
		return $this->ci->session->userdata('role_name');
	}
	
	public function is_admin()
	{
		return strtolower($this->ci->session->userdata('role_name')) == 'admin';
	}
	// 是否登录
	public function is_logged_in()
	{
		return $this->ci->session->userdata('logged_in');
	}

	//如果用户是禁止用户，称这只要求后login()和返回false
	public function is_banned()
	{
		return $this->_banned;
	}
	
	// 被禁止的理由，称这只要求后login()和返回false
	public function get_ban_reason()
	{
		return $this->_ban_reason;
	}
	// Check if user has $roles privilege
	// If $use_role_name TRUE then $roles is name such as 'admin', 'editor', 'etc'
	// else $roles is role_id such as 0, 1, 2
	// If $check_parent is TRUE means if roles not found in user role, it will check if user role parent has that roles
	public function is_role($roles = array(), $use_role_name = TRUE, $check_parent = TRUE)
	{
		$result = FALSE;

		$check_array = array();
		
		if ($check_parent)
		{
			// Add parent roles into check array
			if ($use_role_name)
			{
				$check_array = $this->ci->session->userdata('parent_roles_name');
			}
			else
			{
				$check_array = $this->ci->session->userdata('parent_roles_id');
			}
		}
		
		// Add current role into check array
		if ($use_role_name)
		{
			array_push($check_array, $this->ci->session->userdata('role_name'));
		}
		else
		{
			array_push($check_array, $this->ci->session->userdata('role_id'));
		}
		
		// If $roles not array then we add it into an array
		if ( ! is_array($roles))
		{
			$roles = array($roles);
		}
		
		if ($use_role_name)
		{
			// Convert check array into lowercase since we want case insensitive checking
			for ($i = 0; $i < count($check_array); $i++)
			{
				$check_array[$i] = strtolower($check_array[$i]);
			}
		
			// Convert roles into lowercase since we want insensitive checking
			for ($i = 0; $i < count($roles); $i++)
			{
				$roles[$i] = strtolower($roles[$i]);
			}
		}
		
		// Check if roles exist in check_array
		if (_array_in_array($roles, $check_array))
		{
			$result = TRUE;
		}
		
		return $result;
	}

	// Check if username is available to use, by making sure there is no same username in the database
	public function is_username_available($username)
	{
		$this->ci->load->model('user/users', 'users');
		$this->ci->load->model('user/user_temp', 'user_temp');

		$users = $this->ci->users->check_username($username);
		$temp = $this->ci->user_temp->check_username($username);
		
		return $users->num_rows() + $temp->num_rows() == 0;
	}
	
	// Check if email is available to use, by making sure there is no same email in the database
	public function is_email_available($email)
	{
		$this->ci->load->model('user/users', 'users');
		$this->ci->load->model('user/user_temp', 'user_temp');

		$users = $this->ci->users->check_email($email);
		$temp = $this->ci->user_temp->check_email($email);
		
		return $users->num_rows() + $temp->num_rows() == 0;
	}
	
	/* End of Helper function */

	public function login($login, $password, $remember = TRUE)
	{
		$this->ci->load->model('user/users', 'users');
		$this->ci->load->model('user/user_temp', 'user_temp');
		$this->ci->load->model('user/login_attempts', 'login_attempts');

		$result = FALSE;
				
		if ( ! empty($login) AND ! empty($password))
		{
			if ($this->ci->config->item('login_using_username') AND $this->ci->config->item('login_using_email'))
			{
				$get_user_function = 'get_login';
			}
			else if ($this->ci->config->item('login_using_email'))
			{
				$get_user_function = 'get_user_by_email';
			}			
			else
			{
				$get_user_function = 'get_user_by_username';
			}

			if ($query = $this->ci->users->$get_user_function($login) AND $query->num_rows() == 1)
			{
				$row = $query->row();

				if ($row->banned > 0)
				{
					$this->_banned = TRUE;
					$this->_ban_reason = $row->ban_reason;
				}
				else
				{					
					$password = pass_encode($password);
					$stored_hash = $row->password;

					// Is password matched with hash in database ?
					if (crypt($password, $stored_hash) === $stored_hash)
					{
						$this->_set_session($row); 												
						
						if ($row->newpass)
						{
							$this->ci->users->clear_newpass($row->id); 
						}
						
						if ($remember)
						{
							$this->_create_autologin($row->id);
						}						
						
						$this->_set_last_ip_and_last_login($row->id);
						$this->_clear_login_attempts();
						
						// 登录事件处理 $row->id

						$result = TRUE;
					}
					else						
					{
						$this->_increase_login_attempt();
						$this->_auth_error = '密码错误';
					}
				}				
			}
			elseif ($query = $this->ci->user_temp->$get_user_function($login) AND $query->num_rows() == 1)
			{
				$this->_auth_error = '您的帐户没有被激活，请检查您的电子邮件。';
			}
			else
			{
				$this->_increase_login_attempt();
				$this->_auth_error = '用户名不存在';
			}
		}

		return $result;
	}

	public function logout()
	{
		// 注销事件处理 $this->ci->session->userdata('user_id')
		if ($auto = $this->ci->input->cookie($this->ci->config->item('autologin_cookie_name')))
		{
			$this->ci->load->helper('cookie');
			$this->ci->load->model('user/user_autologin', 'user_autologin');

			$auto = unserialize($auto);			

			$this->ci->user_autologin->delete_key($auto['key_id'], $auto['user_id']);

			set_cookie($this->ci->config->item('autologin_cookie_name'),	'',	-1);
		}
		
		$this->ci->session->sess_destroy();		
	}

	public function register($username, $password, $email)
	{		
		$this->ci->load->model('user/users', 'users');
		$this->ci->load->model('user/user_temp', 'user_temp');
		
		$result = FALSE;

		$new_user = array(			
			'username'		=> $username,			
			'password'		=> crypt(pass_encode($password)),
			'email'			=> $email,
			'last_ip'		=> $this->ci->input->ip_address()
		);

		if ($this->email_activation)
		{
			$new_user['activation_key'] = md5(rand().microtime());
			$insert = $this->ci->user_temp->create_temp($new_user);
		}
		else
		{
			$insert = $this->ci->users->create_user($new_user);
			// 创建资料库待添加 返回注册ID $this->ci->db->insert_id()			
		}
		
		if ($insert)
		{
			$new_user['password'] = $password;
			$result = $new_user;
			
			if ($this->email_activation)
			{
				$this->_sending_activation_email($new_user);
			}
			else
			{
				if ($this->ci->config->item('email_account_details')) 
				{
					$subject = sprintf('%s 帐户的详细资料', $this->ci->config->item('site_name')); 
					
					$content = sprintf($this->ci->config->item('auth_account_content'), $this->ci->config->item('site_name'), $new_user['email'], $new_user['password'], site_url('/user/login/'), $this->ci->config->item('site_name'));
					send_email($email, $subject, $content);														
				}
			}
		}
		
		return $result;
	}

	public function forgot_password($login)
	{
		$result = FALSE;
	
		if ($login)
		{
			$this->ci->load->model('user/users', 'users');

			if ($query = $this->ci->users->get_login($login) AND $query->num_rows() == 1)
			{
				$row = $query->row();
				
				// Check if there is already new password created but waiting to be activated for this login
				if ( ! $row->newpass_key)
				{
					$data['password'] = mt_rand();
					
					$encode = crypt(pass_encode($data['password'])); 

					$data['key'] = md5(rand().microtime());

					$this->ci->users->newpass($row->id, $encode, $data['key']);

					$data['reset_password_uri'] = site_url("/user/reset_password/{$row->username}/{$data['key']}");
					
					$content = sprintf($this->ci->config->item('auth_forgot_password_content'), $this->ci->config->item('site_name'), $data['reset_password_uri'], $data['password'], $data['key'], $this->ci->config->item('site_email'), $this->ci->config->item('site_name'));
					send_email($row->email, '找回密码', $content);
					
					$result = TRUE;
				}
				else
				{
					$this->_auth_error = '你的要求更改密码已经发送。请检查您的电子邮件。';
				}
			}
			else
			{
				$this->_auth_error = '用户名或电子邮件地址不存在。';
			}
		}
		
		return $result;
	}

	public function reset_password($username, $key = '')
	{
		$this->ci->load->model('user/users', 'users');
		$this->ci->load->model('user/user_autologin', 'user_autologin');
		
		$result = FALSE;
		$user_id = 0;
		
		if ($query = $this->ci->users->get_user_by_username($username) AND $query->num_rows() == 1)
		{
			$user_id = $query->row()->id;
			
			if ( ! empty($username) AND ! empty($key) AND $this->ci->users->activate_newpass($user_id, $key) AND $this->ci->db->affected_rows() > 0 )
			{
				$this->ci->user_autologin->clear_keys($user_id);
				$result = TRUE;
			}
		}
		return $result;
	}

	public function activate($username, $key = '')
	{
		$this->ci->load->model('user/users', 'users');
		$this->ci->load->model('user/user_temp', 'user_temp');

		$result = FALSE;
				
		if ($this->email_activation)
		{
			$this->ci->user_temp->prune_temp();
		}

		if ($query = $this->ci->user_temp->activate_user($username, $key) AND $query->num_rows() > 0)
		{
			$row = $query->row_array();
			$del = $row['id'];

			unset($row['id']);
			unset($row['activation_key']);

			if ($this->ci->users->create_user($row))
			{
				// 创建资料库待添加 返回注册ID $this->ci->db->insert_id()
				$this->ci->user_temp->delete_user($del);		
								
				$result = TRUE;
			}
		}

		return $result;
	}

	public function change_password($old_pass, $new_pass)
	{
		$this->ci->load->model('user/users', 'users');
		
		$result = FAlSE;

		if ($query = $this->ci->users->get_user_by_id($this->ci->session->userdata('user_id')) AND $query->num_rows() > 0)
		{
			$row = $query->row();
			$pass = pass_encode($old_pass);

			if (crypt($pass, $row->password) === $row->password)
			{
				$new_pass = crypt(pass_encode($new_pass));

				$this->ci->users->change_password($this->ci->session->userdata('user_id'), $new_pass);
				
				// 修改密码事件处理 $this->ci->session->userdata('user_id'), $new_pass
				
				$result = TRUE;
			}
			else 
			{
				$this->_auth_error = '原始密码错误';
			}
		}
		
		return $result;
	}

	public function change_profile($username = '匿名', $sex = '3', $birthday = '0000-00-00', $flavor = '', $skilled = '')
	{
		$this->ci->load->model('user/users', 'users');
		$result = FAlSE;

		if ($query = $this->ci->users->get_user_by_id($this->ci->session->userdata('user_id')) AND $query->num_rows() > 0)
		{
			$this->ci->users->change_profile($this->ci->session->userdata('user_id'), $username, $sex, $birthday, $flavor, $skilled);
			$result = TRUE;
		}
		
		return $result;
	}

	public function cancel_account($password)
	{
		$this->ci->load->model('user/users', 'users');
		
		$result = FAlSE;
		
		if ($query = $this->ci->users->get_user_by_id($this->ci->session->userdata('user_id')) AND $query->num_rows() > 0)
		{
			$row = $query->row();

			$pass = pass_encode($password);

			if (crypt($pass, $row->password) === $row->password)
			{
				// 删除资料库 空

				// 删除用户
				$result = $this->ci->users->delete_user($this->ci->session->userdata('user_id'));

				$this->logout();
			}
			else
			{
				$this->_auth_error = '密码不正确。';
			}
		}
		
		return $result;
	}
	//发送激活邮件
	public function _sending_activation_email($data)
	{
		$subject = $this->ci->config->item('site_name').' 激活邮件';
		$encode_user = urlencode($data['username']);
		$data['activate_url'] = site_url("/user/activate/{$encode_user}/{$data['activation_key']}");

		$content = sprintf($this->ci->config->item('auth_activate_content'), 
			$this->ci->config->item('site_name'), 
			$data['activate_url'], 
			$this->ci->config->item('email_activation_expire') / 60 / 60, 
			$data['email'], 
			$this->ci->config->item('site_name'));

		send_email($data['email'], $subject, $content);
	}
	/* End of main function */

	//上传头像信息
	public function get_avatar_info($uid){
		if(empty($uid)){
			return false;
		}
		$uid = abs(intval($uid));
		$uid = sprintf("%09d", $uid);
		$dir1 = substr($uid, 0, 3);
		$attdir = "./uploads/user_avatar/".$dir1."/";
		if(!is_dir($attdir)){
			mkdir($attdir,0777);
		}
		$dir2 = substr($uid, 3, 2);
		$attdir = $attdir."".$dir2."/";
		if(!is_dir($attdir)){
			mkdir($attdir,0777);
		}
		$dir3 = substr($uid, 5, 2);
		$attdir = $attdir."".$dir3."/";
		if(!is_dir($attdir)){
			mkdir($attdir,0777);
		}
		return array($attdir,$uid);
	}
	//获取用户头像
	public function get_avatar_dir($uid) {
		if (empty($uid)){
			return false;
		}

	    $uid = sprintf("%09d", intval($uid));
	    $dir1 = substr($uid, 0, 3);
	    $dir2 = substr($uid, 3, 2);
	    $dir3 = substr($uid, 5, 2);

	    $avatar_dir = 'uploads/user_avatar/' . $dir1 . '/' . $dir2 . '/' . $dir3 . '/' . $uid . '.jpg';

	    if (file_exists($avatar_dir)){
	    	return base_url(''. $avatar_dir . '');
	    }else{
	    	return base_url('static/img/avatar.jpg');
	    }
	}
}

/* End of Recaptcha function */